// 设计模式 7 大原则

// 开闭 :当应用的需求改变时，在不修改  软件实体的源码，可以扩展模块的功能，使其满足新的需求(软件实体应当对扩展开放(提供的人)，对修改关闭(使用的人))
// 里氏替换 :如何正确使用继承？ 使用父类的地方可以被完全替换成子类  即(尽量不要重写父类方法 只是添加方法) 如果出现需要重写的 则最好向上再定义更通俗的父类

// 单一职责 : 降低类的复杂度。一个类只负责一项职责，其逻辑肯定要比负责多项职责简单得多。
// 接口隔离 : 接口里面方法 个数不要写太多 ;接口尽量小，但是要有限度。一个接口只服务于一个子模块或业务逻辑。为依赖接口的类定制服务。只提供调用者需要的方法，屏蔽不需要的方法。

// 依赖倒置 :高层模块不应该依赖低层模块，两者都应该依赖其抽象；抽象不应该依赖细节，细节应该依赖抽象 ;其核心思想是：要面向接口编程，不要面向实现编程
// 迪米特 :  最少知道法则 (类与类之间); 自己的逻辑自己实现 不要写到别的类里去  别人只负责调;迪米特 朋友是指: 入参, 方法返回值,成员变量
// 合成复用 :采用组合或聚合复用时，可以将已有对象纳入新对象中，使之成为新对象的一部分，新对象可以调用已有对象的功能，它有以下优点。


package designModel;

// 单例（Singleton）模式：某个类只能生成一个实例，该类提供了一个全局访问点供外部获取该实例，其拓展是有限多例模式。 ☆☆☆☆☆

// 推荐使用  枚举（但是枚举本身不多用）  静态内部类  双重检测 与  饿汉式

public class Singleton {
    public static void main(String[] args) {
        System.out.println("===========饿汉式=============");
        HungrySingleton h1 = HungrySingleton.getInstance();
        HungrySingleton h2= HungrySingleton.getInstance();
        System.out.println(h1==h2);
        System.out.println(h1.hashCode()==h2.hashCode());
        System.out.println("h1和h2是同一个实例");

        System.out.println("===========懒汉式 线程不安全=============");
        LazySingleton l1 =LazySingleton.getInstance();
        LazySingleton l2 =LazySingleton.getInstance();
        System.out.println(l1==l2);

        System.out.println("============枚举式==========");
        LazySingleton5 instance = LazySingleton5.INSTANCE;
        LazySingleton5 instance2 = LazySingleton5.INSTANCE;
        System.out.println(instance == instance2);
    }
}


//饿汉式单例 (可用)： 线程安全  无 懒加载效果 可能会造成性能、内存浪费
 class HungrySingleton {
//  private static final HungrySingleton instance = new HungrySingleton();
    private static final HungrySingleton instance ;
//  类加载时 执行
    static {
        instance = new HungrySingleton();
    }

    private HungrySingleton() {

    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}
// 懒汉式 写法: 线程不安全 (不用这种方式 仅限于单线程)
class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {

    }
    // 提供一个静态的公有方法，当使用到该方法时，才去创建 instance
    // 即懒汉式
    public static LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}


// 懒汉式(线程安全，同步代码块) 不推荐它 效率低 因为每次都会去调getInstance
class LazySingleton2 {
    private static LazySingleton2 instance;

    private LazySingleton2() {}
    // 线程同步 (不允许多个线程同时访问getInstance 方法 只能等待)_
    public static synchronized LazySingleton2 getInstance() {
        if (instance==null){
            instance = new LazySingleton2();
        }
        return instance;
    }

/*    public static LazySingleton2 getInstance() { // 更不靠谱 起不到线程同步的效果 无法解决线程安全
        if (instance == null) {
        // 多个线程已经进来了
            synchronized (LazySingleton2.class) {
                instance = new LazySingleton2();
            }
        }
        return instance;
    }*/
}

// 饿汉式 双重检查(推荐使用) 解决线程安全 解决懒加载问题
 class LazySingleton3 {
    // 声明为 volatile 是为了避免在多线程中,new对象时,指令重排,
    // 造成对象未创建,而判断为非空的情况
    private volatile static LazySingleton3 sInstance = null;

    private LazySingleton3() {
        // 防止反射调用,被创建出多个实例
        if (sInstance != null)
            throw new RuntimeException("instance exist");
    }

    public synchronized static LazySingleton3 getInstance() {
        // 不加锁,判断是否为空, 在锁竞争的情况下,提高性能
        if (sInstance == null) {
            // 只有当为空的时候,加锁创建
            synchronized (LazySingleton3.class) {
                if (sInstance == null)
                    sInstance = new LazySingleton3();
            }
        }
        return sInstance;
    }
}
//饿汉式 静态内部类   (非常好的方式  推荐)
class LazySingleton4{

    private LazySingleton4() {}

    private static class LazySingletonInstance{
        private static final LazySingleton4 instance = new LazySingleton4(); // 类装载时 线程是安全的 jvm做的
    }
    public static LazySingleton4 getInstance(){
        return LazySingletonInstance.instance;
    }
}

// 枚举式  推荐使用(防止反射创建)

 enum LazySingleton5 {
    INSTANCE; // 实例
    public void doSomething() {
        System.out.println("doSomething");
    }

}

